﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Sgml.Validation;
using Engine.Sgml.Validation.Rules;
using System.Diagnostics;
using Engine.Dtd.Schema;
using Engine.Sgml.Merging;
using Engine.Sgml.Assembling;

namespace Engine.Sgml.Allocation {
    /// <summary>
    /// Provides access to objects that can be used to allocate inconsistent elements in a document structure.
    /// </summary>
    internal sealed class AllocationContext {
        private IDictionary<string, IAllocationStrategy> strategies;
        private IDictionary<string, IBlockAllocator> blockAllocators;
        private BlockItemCollection blocks;
        private ValidationRuleCollection rules;

        internal AllocationContext(ValidationRuleCollection rules) {
            Guard.ArgumentNotNull(rules, "rules");
            this.rules = rules;
            this.strategies = new Dictionary<string, IAllocationStrategy>();
            this.blockAllocators = new Dictionary<string, IBlockAllocator>();

            this.blocks = new BlockItemCollection();

            Register(new BlocksAllocationStrategy(), "BlocksAllocation");
            Register(new InnerBlocksAllocationStrategy(), "InnerBlocksAllocation");
            Register(new UncompletedBlocksAllocationStrategy(), "UncompletedBlocksAllocation");

            Register(new BlockAllocator(rules), "BlockAllocator");
            Register(new InBlocksBlockAllocator(), "InBlocksAllocator");
            Register(new SurroundBlockAllocator(), "SurroundBlockAllocator");
            }

        internal void Clear() {
            strategies.Clear();
            }

        public DtdSchema Schema { get; internal set; }

        public BlockItemCollection Blocks {
            get { return blocks; }
            }

        public ValidationRuleCollection Rules {
            get { return rules; }
            }

        /// <summary>
        /// Gets the collection of IAllocationStrategy elements.
        /// </summary>
        public IEnumerable<IAllocationStrategy> Strategies {
            get { return strategies.Values; }
            }

        public IEnumerable<IBlockAllocator> BlockAllocators {
            get { return blockAllocators.Values; }
            }

        /// <summary>
        /// Registers the allocation strategy with the specified name.
        /// </summary>
        /// <param name="strategy">An allocation strategy object.</param>
        /// <param name="name">The name of an registered strategy.</param>
        public void Register(IAllocationStrategy strategy, string name) {
            Guard.ArgumentNotNull(strategy, "strategy");
            Guard.ArgumentNotNullOrEmpty(name, "name");
            strategies[name] = strategy;
            }

        public void Register(IBlockAllocator allocator, string name) {
            Guard.ArgumentNotNull(allocator, "allocator");
            Guard.ArgumentNotNullOrEmpty(name, "name");
            blockAllocators[name] = allocator;
            }

        /// <summary>
        /// Returns the allocation strategy with the specified name.
        /// </summary>
        /// <param name="name">A string that represent the name of strategy.</param>
        /// <returns>The allocation strategy object.</returns>
        public IAllocationStrategy GetStrategy(string name) {
            Guard.ArgumentNotNullOrEmpty(name, "name");
            Debug.Assert(strategies.ContainsKey(name));
            return strategies[name];
            }

        public IBlockAllocator GetBlockAllocator(string name) {
            Guard.ArgumentNotNullOrEmpty(name, "name");
            Debug.Assert(blockAllocators.ContainsKey(name));
            return blockAllocators[name];
            }
        }
    }

